/*
 * Copyright (c) 2010-2012 Zhao.Xiang<z405656232x@163.com> Holding Limited.
 * All rights reserved.
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ims.inv.web.servlet;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import org.ims.inv.model.project.ModelProject;
import org.ims.inv.model.project.ModelProjectMaterial;
import org.ims.inv.model.project.ModelProjectMaterialMeta;
import org.ims.inv.model.system.ModelAppUser;
import org.ims.inv.service.project.ServiceProject;
import org.ims.inv.service.project.ServiceProjectMaterial;
import org.ims.inv.service.system.ServiceAppUser;
import org.ims.inv.web.util.ContextUtil;

import cn.trymore.core.util.UtilApp;
import cn.trymore.core.util.UtilCollection;
import cn.trymore.core.util.UtilDate;
import cn.trymore.core.util.UtilFile;
import cn.trymore.oa.model.system.ModelFileAttach;
import cn.trymore.oa.service.system.ServiceFileAttach;

/**
 * The servlet for file uploading.
 * 
 * @author Jeccy.Zhao
 *
 */
public class ProjectFileUploadServlet
extends HttpServlet
{
	
	/**
	 * Serial UID
	 */
	private static final long serialVersionUID = 4757212133352817333L;
	
	/**
	 * The LOGGER
	 */
	private static final Logger LOGGER = Logger.getLogger(ProjectFileUploadServlet.class);
	
	/**
	 * Status code for success
	 */
	private final String STATUS_CODE_SUCCESS = "1";
	
	/**
	 * Status code for failure
	 */
	private final String STATUS_CODE_FAILURE = "0";
	
	/**
	 * The file attach service.
	 */
	private ServiceFileAttach serviceFileAttach = (ServiceFileAttach) UtilApp.getBean("serviceFileAttach");
	
	/**
	 * The project material service.
	 */
	private ServiceProjectMaterial serviceProjectMaterial = (ServiceProjectMaterial) UtilApp.getBean("serviceProjectMaterial");
	
	/**
	 * The user service.
	 */
	private ServiceAppUser serviceAppUser = (ServiceAppUser) UtilApp.getBean("serviceAppUser");
	
	/**
	 * The project service
	 */
	private ServiceProject serviceProject = (ServiceProject) UtilApp.getBean("serviceProject");
	
	/**
	 * The servlet configuration
	 */
	private ServletConfig servletConfig = null;
	
	/**
	 * The file upload destination path.
	 */
	private String uploadPath = "";
	
	/**
	 * The file upload temporary path.
	 */
	private String tempPath = "";
	
	/**
	 * The file category
	 */
	private final String fileCat = "others";
	
	private ModelProject entityProject;
	
	private ModelProjectMaterial entityMaterial;
	
	private ModelProjectMaterialMeta entityMeta;
	
	private ModelProjectMaterial entityAdditionalMaterial;
	
	private ModelFileAttach entityFile;
	
	public void init(ServletConfig servletConfig) throws ServletException
	{
		this.servletConfig = servletConfig;
		super.init(servletConfig);
	}
	
	/**
	 * Initializations, like:
	 * 
	 * 1. Define the temporary path for file upload.   <br/>
	 * 2. Define the destination path for file upload. <br/>
	 * 3. Create the above directories if not existed. 
	 * 
	 */
	public void init() throws ServletException
	{
		this.uploadPath = servletConfig.getServletContext().getRealPath("/uploads/");
		
		System.out.println(this.uploadPath);
		
		File localFile = new File(this.uploadPath);
		if (!localFile.exists())
		{
			localFile.mkdirs();
		}
		
		this.tempPath = this.uploadPath + "/temp";
		File tmpFile = new File(this.tempPath);
		if (!tmpFile.exists())
		{
			tmpFile.mkdirs();
		}
	}
	
	/*
	 * (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	@SuppressWarnings("unchecked")
	protected void doPost(HttpServletRequest request, 
			HttpServletResponse response) throws ServletException, IOException
	{
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		
		try
		{
			DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
			diskFileItemFactory.setSizeThreshold(4096);
			diskFileItemFactory.setRepository(new File(this.tempPath));
			
			ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
			List<FileItem> fileList = (List<FileItem>) servletFileUpload.parseRequest(request);
			Iterator<FileItem> itor = fileList.iterator();
			Iterator<FileItem> itor1 = fileList.iterator();
			
			String file_type = "";
			while(itor1.hasNext())
			{
				FileItem item = itor1.next();
				if(item.isFormField() && "file_type".equals(item.getFieldName()))
				{
					file_type = item.getString();
				}
			}
			
			FileItem fileItem;
			String newFileName = null;
			while (itor.hasNext())
			{
				fileItem = itor.next();
				
				if (fileItem.getContentType() == null)
				{
					continue;
				}
				
				// obtains the file path and name
				String filePath = fileItem.getName();
				
				String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
				// generates new file name with the current time stamp.
				newFileName = this.fileCat + "/" + UtilFile.generateFilename(fileName);
				// ensure the directory existed before creating the file.
				File dir = new File(this.uploadPath + "/" + newFileName.substring(0, newFileName.lastIndexOf("/") + 1));
				if (!dir.exists())
				{
					dir.mkdirs();
				}
				
				// stream writes to the destination file
				fileItem.write(new File(this.uploadPath + "/" + newFileName));
				
				if (request.getParameter("noattach") == null)
				{
					// storages the file into database.
					this.entityFile = new ModelFileAttach();
					entityFile.setFileName(fileName);
					entityFile.setFilePath(newFileName);
					entityFile.setTotalBytes(Long.valueOf(fileItem.getSize()));
					entityFile.setNote(this.getStrFileSize(fileItem.getSize()));
					entityFile.setFileExt(fileName.substring(fileName.lastIndexOf(".") + 1));
					entityFile.setCreatetime(new Date());
					entityFile.setDelFlag(ModelFileAttach.FLAG_NOT_DEL);
					entityFile.setFileType(!"".equals(file_type)?file_type:this.fileCat);
					
					ModelAppUser user = ContextUtil.getCurrentUser();
					if (user != null)
					{
						entityFile.setCreatorId(Long.valueOf(user.getId()));
						entityFile.setCreator(user.getUsername());
					}
					else
					{
						entityFile.setCreator("Unknow");
					}
					
					if (request.getParameter("todb") != null) 
					{
						this.serviceFileAttach.save(entityFile);
					}
				}
				
				break;
			}
			
			if (request.getParameter("noentity") == null)
			{
				this.saveEntities(fileList);
			}
			
			if (request.getParameter("json") != null)
			{
				String fileId = "";
				if (entityFile != null)
				{
					fileId = entityFile.getId();
				}
				
				response.setContentType("text/html;charset=UTF-8");
				PrintWriter writer = response.getWriter();
				writer.println("{\"status\": 1, \"data\":{\"url\":\"" + newFileName + "\", \"sUrl\":\"" + newFileName + "\", \"fid\":\"" + fileId + "\"}}");
			}
			else
			{
				response.getWriter().write(this.getCallback(STATUS_CODE_SUCCESS));
			}
			
			return;
		}
		catch (Exception e)
		{
			LOGGER.error("Exception raised when uploading file...", e);
		}
		
		response.getWriter().write(this.getCallback(STATUS_CODE_FAILURE));
		return;
		
	}
	
	/**
	 * 
	 * @param fileItems
	 * @param fileAttach
	 * @throws Exception
	 */
	private void saveEntities (List<FileItem> fileItems) throws Exception
	{
		String materialName = null;
		String materialRoot = null;
		String materialType = null;
		
		if (UtilCollection.isNotEmpty(fileItems))
		{
			this.entityMeta = new ModelProjectMaterialMeta();
			
			Iterator<FileItem> itor = fileItems.iterator();
			while (itor.hasNext())
			{
				FileItem item = itor.next();
				
				if (item.isFormField())
				{
					String fieldName = item.getFieldName();
					String fieldValue = item.getString();
					try
					{
						fieldValue = new String(fieldValue.getBytes("ISO-8859-1"), "UTF-8");
					}
					catch (Exception e)
					{
						LOGGER.error("Unsupported encoding from text, " + fieldValue);
					}
					
					if (fieldName.equalsIgnoreCase("poster.id"))
					{
						ModelAppUser user = serviceAppUser.get(fieldValue);
						entityMeta.setPoster(user);
					}
					else if (fieldName.equalsIgnoreCase("postTime"))
					{
						entityMeta.setPostTime(UtilDate.toDate(fieldValue));
					}
					else if (fieldName.equalsIgnoreCase("mid") && Integer.valueOf(fieldValue) > 0)
					{
						this.entityMaterial = serviceProjectMaterial.get(fieldValue);
					}
					else if (fieldName.equalsIgnoreCase("pid") && Integer.valueOf(fieldValue) > 0)
					{
						this.entityProject = serviceProject.get(fieldValue);
					}
					else if (fieldName.equalsIgnoreCase("materialName"))
					{
						materialName = fieldValue;
					}
					else if (fieldName.equalsIgnoreCase("materialRoot"))
					{
						materialRoot = fieldValue;
					}
					else if (fieldName.equalsIgnoreCase("materialType"))
					{
						materialType = fieldValue;
					}
				}
			}
			
			this.serviceFileAttach.save(this.entityFile);
			
			this.entityMeta.setAttachment(this.entityFile);
			this.entityMeta.setCreationTime(new Date());
			
			if (this.entityMaterial == null)
			{
				this.entityAdditionalMaterial = new ModelProjectMaterial();
				this.entityAdditionalMaterial.setMaterialDesc(materialName);
				this.entityAdditionalMaterial.setMaterialName(materialName);
				
				ModelProjectMaterial root = this.serviceProjectMaterial.get(materialRoot);
				this.entityAdditionalMaterial.setMaterialParent(root);
				this.entityAdditionalMaterial.setMaterialType(Integer.valueOf(materialType));
				this.entityAdditionalMaterial.setSequence(root.getMaterialChildren().size() + 1);
				this.entityAdditionalMaterial.setProjectNo(this.entityProject.getProjectNo());
				
				this.entityMeta.setMaterial(this.entityAdditionalMaterial);
				this.entityAdditionalMaterial.addMeta(this.entityMeta);
				root.addSubMaterial(this.entityAdditionalMaterial);
				
				this.serviceProjectMaterial.save(root);
			}
			else
			{
				this.entityMeta.setMaterial(this.entityMaterial);
				this.entityMaterial.addMeta(this.entityMeta);
				this.serviceProjectMaterial.save(entityMaterial);
			}
		}
	}
	
	/**
	 * Obtains call-back string 
	 * 
	 * @param statusCode
	 * @return
	 */
	private String getCallback (String statusCode)
	{
		return "<script type='text/javascript' type='language'>parent.afterUpload('" + statusCode + "');</script>";
	}
	
	/**
	 * Obtains the decimal formated file size.
	 * 
	 * @param size
	 * @return
	 */
	private String getStrFileSize (double size)
	{
		DecimalFormat decimalFormat = new DecimalFormat("0.00");
		
		double byteSize;
		if (size > 1048576.0D)
		{
			byteSize = size / 1048576.0D;
			return decimalFormat.format(byteSize) + " M";
		}
		
		if (size > 1024.0D)
		{
			byteSize = size / 1024.D;
			return decimalFormat.format(byteSize) + " KB";
		}
		
		return size + " bytes";
	}
	
	public void setServletConfig(ServletConfig servletConfig)
	{
		this.servletConfig = servletConfig;
	}

	public ServletConfig getServletConfig()
	{
		return servletConfig;
	}

	public void setUploadPath(String uploadPath)
	{
		this.uploadPath = uploadPath;
	}

	public String getUploadPath()
	{
		return uploadPath;
	}

	public void setTempPath(String tempPath)
	{
		this.tempPath = tempPath;
	}

	public String getTempPath()
	{
		return tempPath;
	}

	public void setServiceFileAttach(ServiceFileAttach serviceFileAttach)
	{
		this.serviceFileAttach = serviceFileAttach;
	}

	public ServiceFileAttach getServiceFileAttach()
	{
		return serviceFileAttach;
	}

	public void setServiceProjectMaterial(ServiceProjectMaterial serviceProjectMaterial)
	{
		this.serviceProjectMaterial = serviceProjectMaterial;
	}

	public ServiceProjectMaterial getServiceProjectMaterial()
	{
		return serviceProjectMaterial;
	}

	public void setEntityProject(ModelProject entityProject)
	{
		this.entityProject = entityProject;
	}

	public ModelProject getEntityProject()
	{
		return entityProject;
	}

	public void setEntityMaterial(ModelProjectMaterial entityMaterial)
	{
		this.entityMaterial = entityMaterial;
	}

	public ModelProjectMaterial getEntityMaterial()
	{
		return entityMaterial;
	}

	public void setEntityMeta(ModelProjectMaterialMeta entityMeta)
	{
		this.entityMeta = entityMeta;
	}

	public ModelProjectMaterialMeta getEntityMeta()
	{
		return entityMeta;
	}

	public void setEntityAdditionalMaterial(ModelProjectMaterial entityAdditionalMaterial)
	{
		this.entityAdditionalMaterial = entityAdditionalMaterial;
	}

	public ModelProjectMaterial getEntityAdditionalMaterial()
	{
		return entityAdditionalMaterial;
	}

}
